package wiki.xsx.core.snowflake.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.client.RestTemplate;
import wiki.xsx.core.snowflake.config.consul.ConsulDiscoveryConfig;
import wiki.xsx.core.snowflake.config.eureka.EurekaDiscoveryConfig;
import wiki.xsx.core.snowflake.config.nacos.NacosDiscoveryConfig;
import wiki.xsx.core.snowflake.config.zookeeper.ZookeeperDiscoveryConfig;
import wiki.xsx.core.snowflake.api.SnowflakeApi;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 雪花算法配置
 * @author xsx
 * @date 2019/11/20
 * @since 1.8
 */
@Slf4j
@Configuration
@ConditionalOnClass({DiscoveryClient.class})
@Import({
        RestTemplate.class,
        LoadBalancerAutoConfiguration.class,
        SnowflakeApi.class,
        ConsulDiscoveryConfig.class,
        EurekaDiscoveryConfig.class,
        NacosDiscoveryConfig.class,
        ZookeeperDiscoveryConfig.class
})
public class SnowflakeAutoConfiguration {

    /**
     * 最大机器码id
     */
    private static final long MAX_WORKER_ID = 31L;
    /**
     * 最大数据中心id
     */
    private static final long MAX_DATA_CENTER_ID = 31L;

    @Bean
    Snowflake snowflake(RestTemplate restTemplate, DiscoveryConfig config, DiscoveryClient discoveryClient) {

        Snowflake snowflake = initSnowflake(restTemplate, config, discoveryClient);
        log.info("【当前机器码id为：{}，数据中心id为：{}】", snowflake.getWorkerId(), snowflake.getDataCenterId());
        return snowflake;
    }

    /**
     * 初始化可用雪花
     */
    private Snowflake initAvailableSnowflake(List<Snowflake> activeSnowflakeList) {
        boolean flag = true;
        for (long i = 0; i < MAX_WORKER_ID; i++) {
            for (long j = 0; j < MAX_DATA_CENTER_ID; j++) {
                for (Snowflake active : activeSnowflakeList) {
                    if (active.getWorkerId()==i&&active.getDataCenterId()==j) {
                        flag = false;
                        break;
                    }
                }
                if (flag) {
                    return new Snowflake(i, j);
                }
                flag = true;
            }
        }
        throw new RuntimeException("no available snowflake");
    }

    /**
     * 初始化雪花
     * @param restTemplate 模板
     * @param config 注册中心配置
     * @param discoveryClient RPC客户端
     * @return 返回雪花实例
     */
    private Snowflake initSnowflake(RestTemplate restTemplate, DiscoveryConfig config, DiscoveryClient discoveryClient) {
        // 是否被占用标记
        boolean flag = false;
        // 获取被占用雪花
        Snowflake usedSnowflake = null;
        // 取一个随机机器码id
        long workerId = ThreadLocalRandom.current().nextLong(0, MAX_WORKER_ID);
        // 取一个随机数据中心id
        long dataCenterId = ThreadLocalRandom.current().nextLong(0, MAX_DATA_CENTER_ID);
        // 被占用雪花集合
        List<Snowflake> activeSnowflakeList = new ArrayList<>(Long.valueOf(MAX_WORKER_ID).intValue());
        // 当前服务所有实例
        List<ServiceInstance> serviceInstances = discoveryClient.getInstances(config.getServiceName());
        // 遍历所有实例（并不包含当前服务）
        for (ServiceInstance serviceInstance : serviceInstances) {
            try{
                // 雪花实例URL
                String used = restTemplate.postForObject(
                        String.format("%s%s", serviceInstance.getUri(), "/snowflake/used"),
                        null,
                        String.class
                );
                if (used!=null) {
                    String[] wdId = used.split("-");
                    usedSnowflake = new Snowflake(Long.parseLong(wdId[0]), Long.parseLong(wdId[1]));
                }
            }catch (Exception e) {
                log.debug("snowflake服务【{}】调用失败，将跳过当前失败服务", serviceInstance.getInstanceId());
                // 超时跳过
                continue;
            }
            // 不为空表示非本服务实例（保险起见，多一个判断）
            if (usedSnowflake!=null) {
                // 随机机器码id与数据中心id是否被占用
                if (workerId==usedSnowflake.getWorkerId()&&dataCenterId==usedSnowflake.getDataCenterId()) {
                    flag = true;
                }
                // 添加到被占用雪花集合
                activeSnowflakeList.add(usedSnowflake);
            }
        }
        Snowflake availableSnowflake;
        // 当前随机机器码id与数据中心id被占用
        if (flag) {
            // 获取可用雪花
            availableSnowflake = initAvailableSnowflake(activeSnowflakeList);
        }else {
            // 初始化可用雪花
            availableSnowflake = new Snowflake(workerId, dataCenterId);
        }
        return availableSnowflake;
    }
}
